package com.shadowtorcher.TableRolePlayingSystem.controller.WebSocket;

import com.shadowtorcher.TableRolePlayingSystem.DataStructure.DataProcessor;
import com.shadowtorcher.TableRolePlayingSystem.DataStructure.DataSegment;
import com.shadowtorcher.TableRolePlayingSystem.util.Logger;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;


public class WebSocketHandler extends AbstractWebSocketHandler {
    private WebSocketPool sessionPool = new WebSocketPool();
    private HashMap<WebSocketSession, WebSocketUser> waitingForHandShakePool = new HashMap<>();

    public static void send(DataSegment dataSegment, WebSocketUser user) {
        String str = DataProcessor.buildString(dataSegment);
        int length = str.length();
        str = String.valueOf(length) + "#" + str;
        try {
            Logger.debug(WebSocketHandShake.class, "发送消息", str, "向", user.getUserName());
            WebSocketSession socketSession = user.getSession();
            synchronized (socketSession) {
                socketSession.sendMessage(new TextMessage(str));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void sendAll(DataSegment dataSegment, WebSocketRoom webSocketRoom) {
        for (WebSocketUser user : webSocketRoom.getAllUser()) {
            WebSocketHandler.send(dataSegment, user);
        }
    }

    public static void sendAllExcept(DataSegment dataSegment, WebSocketRoom webSocketRoom, WebSocketUser webSocketUser) {
        for (WebSocketUser user : webSocketRoom.getAllUser()) {
            if (!user.equals(webSocketUser)) {
                WebSocketHandler.send(dataSegment, user);
            }
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        String string = checkString(message);
        Logger.debug(this.getClass(), "获取消息 " + session + " : " + string);
        DataSegment dataSegment = DataProcessor.buildSegment(string);
        WebSocketUser user = waitingForHandShakePool.get(session);
        if (user == null) {
            user = sessionPool.getUser(session);
        }
        handleMessage(session, user, dataSegment);
    }

    private void handleMessage(WebSocketSession session, WebSocketUser user, DataSegment segment) throws IOException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {
        if (user == null) {
            System.out.println("???");
        }
        switch (user.status) {
            case WebSocketUser.WAITING_TO_HANDSHAKE_STEP1:
            case WebSocketUser.WAITING_TO_HANDSHAKE_STEP2:
            case WebSocketUser.WAITING_TO_HANDSHAKE_STEP3:
                if (WebSocketHandShake.doHandShake(segment, session, user, sessionPool)) {
                    waitingForHandShakePool.remove(session);
                    afterNewUserJoin(user, user.getRoom());
                }
                break;
            case WebSocketUser.LOG_SUCCESS:
                CommandProcessor.doProcess(segment, user, user.getRoom());
                break;
            case WebSocketUser.LOG_FAILURE:
                user.getSession().close();
                break;
        }
    }


    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
        if (session.isOpen()) {
            session.close();
        }
        Logger.debug(this.getClass(), "链接出现了问题", session, exception);
        this.closeConnect(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        Logger.debug(this.getClass(), "链接断开", session);
        this.closeConnect(session);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        super.afterConnectionEstablished(session);
        Logger.debug(this.getClass(), "新链接", session, "等待握手");
        waitingForHandShakePool.put(session, new WebSocketUser());
    }

    private void closeConnect(WebSocketSession session) {
        if (sessionPool.getUser(session) != null) {
            WebSocketUser webSocketUser = sessionPool.getUser(session);
            Logger.debug(this.getClass(), webSocketUser.getUserName(), "登出");
            sessionPool.removeUser(session);
            webSocketUser.logOff();
            afterUserLeave(webSocketUser);
        }
        if (waitingForHandShakePool.get(session) != null) {
            waitingForHandShakePool.remove(session);
        }
    }

    private void afterNewUserJoin(WebSocketUser user, WebSocketRoom room) {
        // 欢迎信息
        DataSegment dataSegment = new DataSegment();
        dataSegment.put("type", "online");
        dataSegment.put("what", "new_user");
        dataSegment.put("data", user.getUserName() + "加入房间");
        dataSegment.put("who", user.getUserName());
        WebSocketHandler.sendAllExcept(dataSegment, user.getRoom(), user);

        // 在线状态
        DataSegment onlineStatus = new DataSegment();
        ArrayList<String> players = new ArrayList<>();
        for (WebSocketUser u : user.getRoom().getAllUser()) {
            players.add(u.getUserName());
        }
        onlineStatus.put("type", "online");
        onlineStatus.put("what", "update_user_list");
        onlineStatus.put("dm", room.getDmName());
        onlineStatus.put("players", players);
        WebSocketHandler.send(onlineStatus, user);

        // 地图信息
        if (!"".equals(room.getGameMap().getRoomDate())) {
            DataSegment mapData = new DataSegment();
            mapData.put("type", "camera");
            mapData.put("what", "hotupdate");
            mapData.put("data", room.getGameMap().getRoomDate());
            WebSocketHandler.send(mapData, user);
        }

        // 聊天历史
        DataSegment log = new DataSegment();
        log.put("type", "online");
        log.put("what", "current");
        log.put("serial", String.valueOf(SaveProcessor.getCurrentByRoom(user.getRoomID())));
        WebSocketHandler.send(log, user);
    }

    private void afterUserLeave(WebSocketUser user) {
        DataSegment dataSegment = new DataSegment();
        dataSegment.put("type", "online");
        dataSegment.put("what", "user_leave");
        dataSegment.put("data", user.getUserName() + "离开房间");
        dataSegment.put("who", user.getUserName());
        WebSocketHandler.sendAll(dataSegment, user.getRoom());
    }


    private String checkString(TextMessage textMessage) throws UnsupportedEncodingException {
        String str = new String(textMessage.asBytes(), "UTF-8");
        String[] strings = str.split("#");
        int length = Integer.parseInt(strings[0]);
        str = str.substring(strings[0].length() + 1, str.length());
        if (str.length() == length)
            return str;
        else
            return str;
    }
}
